
#ifndef _VARARRAY_HPP_
#define _VARARRAY_HPP_

#include <array>
#include <cassert>
#include <cstring>
#include <initializer_list>
#include <string>
// #include <type_traits>
// FORTRAN 格式的索引是 列优先 如 a(2,3,4) 为2行3列4页
// 即一列有两个元素，一行有3个元素  页有四个（页）

// c 和 fortran 格式混搭了
// 除了Freconstruct 其余 c 均可用 只有先声明，后设置(用 Freconstruct
// 设置Fortran格式)

// c 格式为 2页 3行 四列  注意区分 行优先
// #define _FORTRAN_INDEX_
// #define _C_INDEX_

// options = 1 代表行优先  =0 为列优先
/**
 * @brief 只是简单数组  不提供其他操作
 *
 * 不支持负的索引
 *
 * @tparam T 数据类型
 * @tparam D 维度
 * @tparam 1  1 代表行优先 C 模式  =0 为列优先 fortran模式 默认行优先
 * 接口都一样，如果看上面 01 分类和 constexpr if 不顺眼可以分别实现
 */
template <typename T, int D, int options = 1>
class VarArray
{
public:
  typedef int index_t;
  typedef T *iter;
  typedef const T *const_iter;
  typedef T value_type;
  typedef T &reference;
  typedef const T &const_reference;
  // using iter = T *;
  // using const_iter = const T *;
  // using value_type = T;

  // 构造函数和拷贝函数
  VarArray() = default;
  VarArray(index_t i, index_t j = 0, index_t k = 0, index_t m = 0);
  VarArray(std::initializer_list<index_t> dimList);
  VarArray(const VarArray &src);
  VarArray(VarArray &&src);
  VarArray &operator=(const VarArray &src);
  VarArray &operator=(VarArray &&src);
  ~VarArray() { delete[] elem; }

  // 获取相应维度，  c 模式   0 1 2 3
  template <int DD>
  int Dim() const
  {
    static_assert(DD < D, "exceed the dimension you have defined\n");
    return dim[DD];
  }
  // 0 1 2 3
  int Dim(int i) const
  {
    assert(i < D && -1 < i);
    return dim[i];
  }
  // fortran 格式取值
  T &operator()(index_t i) noexcept
  {
    static_assert(D == 1, "one index, one dimension");
    assert(-1 < i && i < dim[0]);
    return elem[i];
  }

  T &operator()(index_t i, index_t j) noexcept
  {
    static_assert(D == 2, "two index, two dimension");
    assert(-1 < i && i < dim[0] && -1 < j && j < dim[1]);
    if constexpr (options)
      return elem[i * dim[1] + j];
    else
      return elem[j * dim[0] + i];
  }
  T &operator()(index_t i, index_t j, index_t k) noexcept
  {
    static_assert(D == 3, "three index, three dimension");
    assert(-1 < i && i < dim[0] && -1 < j && j < dim[1] && -1 < k &&
           k < dim[2]);
    if constexpr (options)
      return elem[(i * dim[1] + j) * dim[2] + k];
    else
      return elem[(k * dim[1] + j) * dim[0] + i];
  }

  T &operator()(index_t i, index_t j, index_t k, index_t m) noexcept
  {
    static_assert(D == 4, "four index, four dimension");
    assert(-1 < i && i < dim[0] && -1 < j && j < dim[1] && -1 < k &&
           k < dim[2] && -1 < m && m < dim[3]);
    if constexpr (options)
      return elem[((i * dim[1] + j) * dim[2] + k) * dim[3] + m];
    else
      return elem[((m * dim[2] + k) * dim[1] + j) * dim[0] + i];
  }
  inline const_reference operator()(index_t i) const noexcept;
  inline const_reference operator()(index_t i, index_t j) const noexcept;
  inline const_reference operator()(index_t i, index_t j, index_t k) const
      noexcept;
  inline const_reference operator()(index_t i, index_t j, index_t k,
                                    index_t m) const noexcept;

  // 重新分配内存和各维度大小  这个函数使用不频繁，可以简写
  inline bool ReConstruct(int i) noexcept;
  inline bool ReConstruct(int i, int j) noexcept;
  inline bool ReConstruct(int i, int j, int k) noexcept;
  inline bool ReConstruct(int i, int j, int k, int m) noexcept;
  // 跟复制函数是不同的，复制函数会清空原来的内存
  inline void CopyValueFrom(const VarArray &src) noexcept;
  inline void MoveValueFrom(VarArray &&src) noexcept;
  inline VarArray &AssignValue(T val) noexcept;
  inline VarArray &operator=(T d) noexcept;

  inline VarArray &operator*=(T fac) noexcept;
  inline VarArray &operator/=(T fac) noexcept;
  inline VarArray &operator+=(T fac) noexcept;
  inline VarArray &operator-=(T fac) noexcept;

  iter begin() noexcept { return elem; }
  const_iter begin() const noexcept { return elem; }
  iter end() noexcept { return elem + sz; }
  const_iter end() const noexcept { return elem + sz; }
  index_t size() const noexcept { return sz; }
  iter data() noexcept { return elem; }
  const_iter data() const noexcept { return elem; }

private:
  // 默认从0开始
  std::array<int, D> dim{};
  index_t sz{};
  T *elem{};
  // 添加引用指针试一试
  mutable bool owner{true};

  void copy_from(const VarArray &src);
  void move_from(VarArray &src);
};

template <typename T, int D, int options>
void VarArray<T, D, options>::copy_from(const VarArray &src)
{
  dim = src.dim;
  sz = src.sz;
  elem = new T[sz];
  assert(elem);
  memcpy(elem, src.elem, sizeof(T) * sz);
}
template <typename T, int D, int options>
void VarArray<T, D, options>::move_from(VarArray &src)
{
  dim = src.dim;
  sz = src.sz;
  elem = src.elem;
  src.sz = 0;
  src.elem = nullptr;
  src.dim[0] = 0;
}
/**
 * @brief Construct a new Var Array< T,  D>:: Var Array object
 *      唯一的可以指定维度初始化
 * @tparam T
 * @tparam D
 * @param dimList
 */
template <typename T, int D, int options>
VarArray<T, D, options>::VarArray(std::initializer_list<index_t> dimList)
{
  index_t size = dimList.size();
  assert(D == size);
  const index_t *beg = dimList.begin();
  index_t tmp{1};
  for (index_t i = 0; i < size; ++i)
  {
    dim[i] = *beg++;
    tmp *= dim[i];
  }
  sz = tmp;
  elem = new T[sz];
  assert(elem);
}
// 拷贝构造函数
template <typename T, int D, int options>
VarArray<T, D, options>::VarArray(const VarArray &src)
{
  copy_from(src);
}
// 移动构造函数
template <typename T, int D, int options>
VarArray<T, D, options>::VarArray(VarArray &&src)
{
  move_from(src);
}
template <typename T, int D, int options>
VarArray<T, D, options>::VarArray(index_t i, index_t j, index_t k, index_t m)
{
  sz = 1;
  // 这样写的话判断条件没有那么严格了
  switch (D)
  {
  case 4:
    assert(m > 0);
    dim[3] = m;
    sz = sz * m;
  case 3:
    assert(k > 0);
    dim[2] = k;
    sz = sz * k;
  case 2:
    assert(j > 0);
    dim[1] = j;
    sz = sz * j;
  case 1:
    dim[0] = i;
    sz = sz * i;
    break;
  default:
    assert(D < 5 && 0 < D);
    break;
  }
  elem = new T[sz];
}

// 赋值运算符
template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::
operator=(const VarArray &src)
{
  if (this == &src)
    return *this;
  delete[] elem;
  copy_from(src);
  return *this;
}
// 移动赋值运算符
template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::operator=(VarArray &&src)
{
  if (this == &src)
    return src;
  delete[] elem;
  move_from(src);
}

template <typename T, int D, int options>
const T &VarArray<T, D, options>::operator()(index_t i) const noexcept
{
  static_assert(D == 1, "one index, one dimension");
  assert(-1 < i && i < dim[0]);
  return elem[i];
}
template <typename T, int D, int options>
const T &VarArray<T, D, options>::operator()(index_t i, index_t j) const
    noexcept
{
  static_assert(D == 2, "two index, two dimension");
  assert(-1 < i && i < dim[0] && -1 < j && j < dim[1]);
  if constexpr (options)
    return elem[i * dim[1] + j];
  else
    return elem[j * dim[0] + i];
}
template <typename T, int D, int options>
const T &VarArray<T, D, options>::operator()(index_t i, index_t j,
                                             index_t k) const noexcept
{
  static_assert(D == 3, "three index, three dimension");
  assert(-1 < i && i < dim[0] && -1 < j && j < dim[1] && -1 < k && k < dim[2]);
  if constexpr (options)
    return elem[(i * dim[1] + j) * dim[2] + k];
  else
    return elem[(k * dim[1] + j) * dim[0] + i];
}
template <typename T, int D, int options>
const T &VarArray<T, D, options>::operator()(index_t i, index_t j, index_t k,
                                             index_t m) const noexcept
{
  static_assert(D == 4, "four index, four dimension");
  assert(-1 < i && i < dim[0] && -1 < j && j < dim[1] && -1 < k && k < dim[2] &&
         -1 < m && m < dim[3]);
  if constexpr (options)
    return elem[((i * dim[1] + j) * dim[2] + k) * dim[3] + m];
  else
    return elem[((m * dim[2] + k) * dim[1] + j) * dim[0] + i];
}
template <typename T, int D, int options>
bool VarArray<T, D, options>::ReConstruct(int i) noexcept
{
  static_assert(D == 1, "not match the dimension");
  delete[] elem;
  sz = dim[0] = i;
  return elem = new T[i];
}
template <typename T, int D, int options>
bool VarArray<T, D, options>::ReConstruct(int i, int j) noexcept
{
  static_assert(D == 2, "not match the dimension");
  delete[] elem;
  dim[0] = i;
  dim[1] = j;
  sz = i * j;
  return elem = new T[sz];
}
template <typename T, int D, int options>
bool VarArray<T, D, options>::ReConstruct(int i, int j, int k) noexcept
{
  static_assert(D == 3, "not match the dimension");
  delete[] elem;
  dim[0] = i;
  dim[1] = j;
  dim[2] = k;
  sz = i * j * k;
  return elem = new T[sz];
}
template <typename T, int D, int options>
bool VarArray<T, D, options>::ReConstruct(int i, int j, int k, int m) noexcept
{
  static_assert(D == 4, "not match the dimension");
  delete[] elem;
  dim[0] = i;
  dim[1] = j;
  dim[2] = k;
  dim[3] = m;
  sz = i * j * k * m;
  return elem = new T[sz];
}

template <typename T, int D, int options>
void VarArray<T, D, options>::CopyValueFrom(const VarArray &src) noexcept
{
  if (this == &src)
    return;
  assert(dim == src.dim);
  memcpy(elem, src.elem, sizeof(T) * sz);
}
template <typename T, int D, int options>
void VarArray<T, D, options>::MoveValueFrom(VarArray &&src) noexcept
{
  if (this == &src)
    return;
  assert(dim == src.dim);
  delete[] elem;
  elem = src.elem;
  sz = src.dim[0] = 0;
}
template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::
operator*=(T fac) noexcept
{
  for (index_t i = 0; i < sz; ++i)
    elem[i] *= fac;
  return *this;
}
template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::
operator/=(T fac) noexcept
{
  for (index_t i = 0; i < sz; ++i)
    elem[i] /= fac;
  return *this;
}
template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::
operator+=(T fac) noexcept
{
  for (index_t i = 0; i < sz; ++i)
    elem[i] += fac;
  return *this;
}
template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::
operator-=(T fac) noexcept
{
  for (index_t i = 0; i < sz; ++i)
    elem[i] -= fac;
  return *this;
}
template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::AssignValue(T val) noexcept
{
  for (index_t i = 0; i < sz; ++i)
    elem[i] = val;
  return *this;
}

template <typename T, int D, int options>
VarArray<T, D, options> &VarArray<T, D, options>::operator=(T d) noexcept
{
  for (index_t i = 0; i < sz; ++i)
    elem[i] = d;
  return *this;
}

#endif